home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Internet Tools 1993 July / Internet Tools.iso / RockRidge / info-service / gopher / Unix / sgopher0.3 / sgopher.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-05-10  |  18.7 KB  |  930 lines

  1. /*
  2.     sgopher.c - a very simple client for the internet gopher
  3.  
  4.     author      : Sean Fuller (fuller@aedc-vax.af.mil)
  5.                   Arnold Engineering and Development Center
  6.  
  7.     This software is provided "as is" without express or
  8.     implied warranty.  In no event shall the author be liable
  9.     for damages of any kind arising from or in connection
  10.     with the use of this software.
  11.  
  12.     4/27/1993 - Wrote and released initial version.
  13.     4/28/1993 - Added connection information logging and
  14.                 pulled configuration information out to
  15.                 the make file. Also added help command.
  16.                 Added ability to mail received text.
  17.     4/29/1993 - Added support for gopher indexes.
  18.                 Added ongoing menu download indication.
  19.                 Fixed empty menu retrieval.
  20.                 Moved ask up so not implicitly declared.
  21.     5/3/1993 -  Gopher mail now from GOPHADDR and logged.
  22.                 Added 'Subject:' and 'To:' line in mail sent.
  23.     5/4/1993 -  now porting to VMS
  24.     5/5/1993 -  got it to compile and run on VMS but no items found
  25.                 getting readline errors on server
  26.     5/6/1993 -  Modified for ULTRIX compilation.
  27.                 fixed it so that it doesn't exit on bad connections.
  28.                 fixed index retrieval.
  29.     5/7/1993 -  Added code for doing timeout.
  30.     5/10/1993 - Added = option for retrieval information.
  31. */
  32.  
  33. #include "conf.h"
  34.  
  35. #ifdef VMS
  36. #define read netread
  37. #define write netwrite
  38. #endif
  39.  
  40. #include <stdio.h>
  41. #include <ctype.h>
  42. #ifdef VMS
  43. #include <stdlib.h>
  44. #include "twg$tcp:[netdist.include.sys]types.h"
  45. #include "twg$tcp:[netdist.include.sys]socket.h"
  46. #include "twg$tcp:[netdist.include.sys]time.h"
  47. #include "twg$tcp:[netdist.include.netinet]in.h"
  48. #include "twg$tcp:[netdist.include.arpa]inet.h"
  49. #include "twg$tcp:[netdist.include]netdb.h"
  50. #else
  51. #include <sys/types.h>
  52. #include <sys/socket.h>
  53. #include <netinet/in.h>
  54. #include <arpa/inet.h>
  55. #include <netdb.h>
  56. #endif
  57. #include <string.h>
  58. #include <time.h>
  59. #ifdef TIMEOUT
  60. #ifdef VMS
  61. #include "twg$tcp:[netdist.include.sys]signal.h"
  62. #else
  63. #include <sys/signal.h>
  64. #endif
  65. #endif
  66.  
  67. #define BUFSIZE 256
  68. char buf[BUFSIZE];
  69. char title[BUFSIZE];
  70. int fd;
  71. int lines_per_page = 22;
  72.  
  73. struct gopher_object;
  74. struct gopher_object {
  75.     char *name;
  76.     char *path;
  77.     char *host;
  78.     char *port;
  79.     char viewed;
  80.     struct gopher_object *next;
  81. };
  82.  
  83. struct stack_item;
  84. struct stack_item {
  85.     struct gopher_object *item;
  86.     int top;
  87.     char title[BUFSIZE];
  88.     struct stack_item *next;
  89. };
  90.  
  91. struct gopher_object root_object =
  92. {
  93.     "Root Connection", "", HOST, PORT, 0, NULL
  94. };
  95.  
  96. struct stack_item *gopher_stack = NULL;
  97.  
  98. #ifdef NEED_STRDUP
  99. char *
  100. #ifdef KRC
  101. strdup(s)
  102.     char *s;
  103. #else
  104. strdup(char *s)
  105. #endif
  106. {
  107.     char *d;
  108.     d = malloc(sizeof(char) * (strlen(s) + 1));
  109.     if (d == NULL)
  110.     {
  111.         printf("Error: out of memory\n");
  112.         exit(0);
  113.     }
  114.     strcpy(d, s);
  115.     return d;
  116. }
  117. #endif
  118.  
  119. #ifdef TIMEOUT
  120. /*
  121.     handle_sigalrm - received SIGALRM signal, do cleanup and exit.
  122. */
  123. void
  124. #ifdef KRC
  125. handle_alarm(sig, code, sc)
  126.     int sig, code;
  127.     struct sigcontext *sc;
  128. #else
  129. handle_alarm(int sig, int code, struct sigcontext *sc)
  130. #endif
  131. {
  132.     printf("\r\n");
  133.     printf("Inactive for %d minute%s.  ", TIMEOUT, (TIMEOUT > 1)?"s":"");
  134.     printf("Terminating Gopher client...\r\n");
  135.     exit(0);
  136. }
  137. #endif
  138.  
  139. /*
  140.     user_input - get string from the user - may use timeout
  141. */
  142. char *
  143. #ifdef KRC
  144. user_input(buf, bufsize, fp)
  145.     char *buf;
  146.     int bufsize;
  147.     FILE *fp;
  148. #else
  149. user_input(char *buf, int bufsize, FILE *fp)
  150. #endif
  151. {
  152.     char *s;
  153. #ifdef TIMEOUT
  154.     alarm(TIMEOUT * 60); /* set timeout alarm */
  155. #endif
  156.     s = fgets(buf, BUFSIZE, stdin);
  157. #ifdef TIMEOUT
  158.     alarm(0); /* reset timeout alarm */
  159. #endif
  160. #ifdef VMS
  161.     printf("\n");
  162. #endif
  163.     return s;
  164. }
  165.  
  166. /*
  167.     user_wait - wait for user to press enter
  168. */
  169. void user_wait()
  170. {
  171.     printf("press <return> to continue: ");
  172.     fflush(stdout);
  173.     user_input(buf, BUFSIZE, stdin);
  174. }
  175.  
  176. /*
  177.     find_item - return item in menu given number
  178. */
  179. struct gopher_object *
  180. #ifdef KRC
  181. find_item(g, num)
  182.     struct gopher_object *g;
  183.     char *num;
  184. #else
  185. find_item(struct gopher_object *g, char *num)
  186. #endif
  187. {
  188.     int i;
  189.     i = atoi(num);
  190.     if (i <= 0)
  191.     {
  192.         printf("Item numbers must be greater than 0.\r\n");
  193.         user_wait();
  194.         return NULL;
  195.     }
  196.     while (i > 1 && g)
  197.     {
  198.         i--;
  199.         g = g->next;
  200.     }
  201.     if (g == NULL)
  202.     {
  203.         printf("Item number is too big.\r\n");
  204.         user_wait();
  205.         return NULL;
  206.     }
  207.     return g;
  208. }
  209.  
  210. #ifdef LOGFILE
  211. /*
  212.     get_client_info - get the ip address and hostname of originator
  213.         and write it out to the log file
  214. */
  215. void get_client_info()
  216. {
  217.     char ipnum[256];
  218.     char name[256];
  219.     char logtime[256];
  220.         struct sockaddr_in sa;
  221.         int length;
  222.         int rc;
  223.     FILE *fp;
  224.     time_t the_time;
  225.         u_long net_addr;
  226.         struct hostent *hp;
  227.         length = sizeof(sa);
  228.         rc = getpeername(0, &sa, &length);
  229.         if (rc != 0) strcpy(ipnum, "localhost");
  230.         else strcpy(ipnum, inet_ntoa(sa.sin_addr));
  231.     hp = gethostbyaddr(&sa.sin_addr, sizeof(sa.sin_addr), AF_INET);
  232.     the_time = time(0);
  233.     strcpy(logtime, ctime(&the_time));
  234.     if (strlen(logtime) > 0) logtime[strlen(logtime) - 1] = 0;
  235.     if (hp != NULL)
  236.     {
  237.         printf("Connected from %s(%s) at %s\r\n", hp->h_name, ipnum,
  238.             logtime);
  239.         strcpy(name, hp->h_name);
  240.     }
  241.     else
  242.     {
  243.         printf("Connected from %s at %s\r\n", ipnum, logtime);
  244.         strcpy(name, "unknown");
  245.     }
  246.     fp = fopen(LOGFILE, "a");
  247.     if (fp == NULL) return;
  248.     fprintf(fp, "%s %s %s\n", logtime, ipnum, name);
  249.     fclose(fp);
  250. }
  251. #endif
  252.  
  253. /*
  254.     push_menu - push menu and top item on stack
  255. */
  256. void
  257. #ifdef KRC
  258. push_menu(g, top)
  259.     struct gopher_object *g;
  260.     int top;
  261. #else
  262. push_menu(struct gopher_object *g, int top)
  263. #endif
  264. {
  265.     struct stack_item *s;
  266.     s = (struct stack_item *)malloc(sizeof(struct stack_item));
  267.     if (s == NULL)
  268.     {
  269.         printf("Error: out of memory\r\n");
  270.         return;
  271.     }
  272.     s->item = g;
  273.     s->top = top;
  274.     s->next = gopher_stack;
  275.     strcpy(s->title, title);
  276.     gopher_stack = s;
  277. }
  278.  
  279. /*
  280.     pop_menu - pop previous menu and top item from stack
  281. */
  282. struct gopher_object *
  283. #ifdef KRC
  284. pop_menu(top)
  285.     int *top;
  286. #else
  287. pop_menu(int *top)
  288. #endif
  289. {
  290.     struct gopher_object *g;
  291.     struct stack_item *i;
  292.     if (gopher_stack == NULL) return NULL;
  293.     i = gopher_stack;
  294.     g = i->item;
  295.     gopher_stack = i->next;
  296.     *top = i->top;
  297.     strcpy(title, i->title);
  298.     free(i);
  299.     return g;
  300. }
  301.  
  302. /*
  303.     readport - read a line in from the socket to the gopher server
  304. */
  305. int readport()
  306. {
  307.     int i, l;
  308.     char *s = buf;
  309.     l = 0;
  310.     while ((i = read(fd, s, 1)) > 0)
  311.     {
  312.         l += i;
  313.         if (*s == '\n') break;
  314.         if (*s != '\r') s++;
  315.     }
  316.     *s = 0;
  317.     return l;
  318. }
  319.  
  320. /*
  321.     close_port - close the connection to the gopher server
  322. */
  323. void close_port()
  324. {
  325.     close(fd);
  326. }
  327.  
  328. /*
  329.     ask - make the connection to the gopher server and request item
  330. */
  331. int
  332. #ifdef KRC
  333. ask(g)
  334.     struct gopher_object *g;
  335. #else
  336. ask(struct gopher_object *g)
  337. #endif
  338. {
  339.     struct sockaddr_in sin;
  340.     struct hostent *host;
  341.     int port;
  342.     printf("Connecting...\r\n");
  343.     fflush(stdout);
  344.     bzero((char*)&sin, sizeof(sin));
  345.     port = atoi(g->port);
  346.     sin.sin_port = htons(port);
  347.     host = gethostbyname(g->host);
  348.     if (host == NULL)
  349.     {
  350.         sin.sin_addr.s_addr = inet_addr(g->host);
  351.     }
  352.     else
  353.     {
  354.         memcpy(&sin.sin_addr.s_addr, host->h_addr_list[0],
  355.             host->h_length);
  356.     }
  357.     sin.sin_family = AF_INET;
  358.     fd = socket(AF_INET, SOCK_STREAM, 0);
  359.     if (fd < 0)
  360.     {
  361.         printf("Error: gopher socket unavailable\r\n");
  362.         return 0;
  363.     }
  364.     if (connect(fd, (struct sockaddr *)&sin, sizeof(sin)) < 0)
  365.     {
  366.         printf("Error: gopher server unavailable\r\n");
  367.         return 0;
  368.     }
  369.     if (write(fd, g->path, strlen(g->path)) == -1)
  370.     {
  371.         printf("Error: write error\r\n");
  372.         return 0;
  373.     }
  374.     if (write(fd, "\n", 1) == -1)
  375.     {
  376.         printf("Error: write error\r\n");
  377.         return 0;
  378.     }
  379.     return 1;
  380. }
  381.  
  382. /*
  383.     mail - Mail gopher item to given email address.
  384.         There is no check to see if it worked.
  385. */
  386. void
  387. #ifdef KRC
  388. mail(g, email)
  389.     struct gopher_object *g;
  390.     char *email;
  391. #else
  392. mail(struct gopher_object *g, char *email)
  393. #endif
  394. {
  395.     char buf[BUFSIZE];
  396.     struct sockaddr_in sin;
  397.     struct hostent *host;
  398.     char c;
  399.     int mfd;
  400.     FILE *fp;
  401.     /*
  402.         open socket to mailer port on gopher host
  403.     */
  404.     printf("Sending Mail...\r\n");
  405.     fflush(stdout);
  406.     bzero((char*)&sin, sizeof(sin));
  407.     sin.sin_port = htons(25);
  408.     host = gethostbyname(HOST);
  409.     if (host == NULL)
  410.     {
  411.         sin.sin_addr.s_addr = inet_addr(g->host);
  412.     }
  413.     else
  414.     {
  415.         memcpy(&sin.sin_addr.s_addr, host->h_addr_list[0],
  416.             host->h_length);
  417.     }
  418.     sin.sin_family = AF_INET;
  419.     mfd = socket(AF_INET, SOCK_STREAM, 0);
  420.     if (mfd < 0)
  421.     {
  422.         printf("Error: mail socket unavailable\r\n");
  423.         return;
  424.     }
  425.     if (connect(mfd, (struct sockaddr *)&sin, sizeof(sin)) < 0)
  426.     {
  427.         printf("Error: mail server unavailable\r\n");
  428.         return;
  429.     }
  430.     /*
  431.         send header information
  432.     */
  433.     sprintf(buf, "HELO %s\n", HOST);
  434.     write(mfd, buf, strlen(buf));
  435.     sprintf(buf, "MAIL From:<%s>\n", GOPHADDR);
  436.     write(mfd, buf, strlen(buf));
  437.     sprintf(buf, "RCPT To:<%s>\n", email);
  438.     write(mfd, buf, strlen(buf));
  439.     sprintf(buf, "DATA\n");
  440.     write(mfd, buf, strlen(buf));
  441.     sprintf(buf, "To: %s\n", email);
  442.     write(mfd, buf, strlen(buf));
  443.     sprintf(buf, "Subject: Gopher: %s\n", g->name + 1);
  444.     write(mfd, buf, strlen(buf));
  445.     if (ask(g))
  446.     {
  447.         while (read(fd, &c, 1) > 0)
  448.         {
  449.             write(mfd, &c, 1);
  450.         }
  451.         close_port();
  452.     }
  453.     sprintf(buf, "\n.\n");
  454.     write(fd, buf, strlen(buf));
  455.     sprintf(buf, "BYE\n");
  456.     write(fd, buf, strlen(buf));
  457.     printf("Message sent.\r\n");
  458.     fflush(stdout);
  459.     close(mfd);
  460. #ifdef LOGFILE
  461.     /*
  462.         Log mail sent.
  463.     */
  464.     fp = fopen(LOGFILE, "a");
  465.     if (fp == NULL) return;
  466.     fprintf(fp, "Sending %s %s %s to %s\n",
  467.         g->path, g->host, g->port, email);
  468.     fclose(fp);
  469. #endif
  470. }
  471.  
  472. /*
  473.     parse_menu_item - parse the menu item returned by gopher
  474. */
  475. struct gopher_object *
  476. #ifdef KRC
  477. parse_menu_item(buf)
  478.     char *buf;
  479. #else
  480. parse_menu_item(char *buf)
  481. #endif
  482. {
  483.     struct gopher_object *g;
  484.     char *s[4];
  485.     int i;
  486.     char *p;
  487.     g = (struct gopher_object *)malloc(sizeof(struct gopher_object));
  488.     if (g == NULL)
  489.     {
  490.         printf("Error: out of memory\r\n");
  491.         return NULL;
  492.     }
  493.     p = buf;
  494.     i = 0;
  495.     while (*p && i < 4)
  496.     {
  497.         s[i] = p;
  498.         while (*p && *p != '\t') p++;
  499.         if (*p)
  500.         {
  501.             *p = 0;
  502.             p++;
  503.             i++;
  504.         }
  505.     }
  506.     g->name = strdup(s[0]);
  507.     g->path = strdup(s[1]);
  508.     g->host = strdup(s[2]);
  509.     g->port = strdup(s[3]);
  510.     g->next = NULL;
  511.     g->viewed = ' ';
  512.     if (strlen(g->name) > 65) g->name[65] = 0;
  513.     return g;
  514. }
  515.  
  516. /*
  517.     get_text - Get text document and display it.
  518.         The gopher port is kept open during reading.
  519.         Lines are truncated to 79 columns.
  520. */
  521. void
  522. #ifdef KRC
  523. get_text(g)
  524.     struct gopher_object *g;
  525. #else
  526. get_text(struct gopher_object *g)
  527. #endif
  528. {
  529.     char input[BUFSIZE];
  530.     int i;
  531.     if (ask(g) == 0) return;
  532.     printf("Getting text...\r\n");
  533.     fflush(stdout);
  534.     i = -1;
  535.     while (readport() > 0 && strcmp(buf, "."))
  536.     {
  537.         i++;
  538.         if (i > lines_per_page)
  539.         {
  540.             printf("press <enter> for more or <q> to quit: ");
  541.             fflush(stdout);
  542.             user_input(input, BUFSIZE, stdin);
  543.             if (*input == 'q') break;
  544.             i = 0;
  545.             printf("\r\n");
  546.         }
  547.         buf[79] = 0;
  548.         printf("%s\r\n", buf);
  549.     }
  550.     printf("press <enter> to continue or <m> to mail: ");
  551.     fflush(stdout);
  552.     user_input(input, BUFSIZE, stdin);
  553.     close_port();
  554.     if (*input == 'm')
  555.     {
  556.         printf("enter email address: ");
  557.         fflush(stdout);
  558.         user_input(input, BUFSIZE, stdin);
  559.         for (i=strlen(input) - 1; i>=0; i--)
  560.         {
  561.             if (input[i] == '\r' || input[i] == '\n')
  562.                 input[i] = 0;
  563.         }
  564.         mail(g, input);
  565.     }
  566. }
  567.  
  568. /*
  569.     get_menu - given path get menu
  570. */
  571. struct gopher_object *
  572. #ifdef KRC
  573. get_menu(g)
  574.     struct gopher_object *g;
  575. #else
  576. get_menu(struct gopher_object *g)
  577. #endif
  578. {
  579.     char group[BUFSIZE];
  580.     int i;
  581.     struct gopher_object *first = NULL;
  582.     struct gopher_object *current = NULL;
  583.     struct gopher_object *new;
  584.     if (ask(g))
  585.     {
  586.         printf("Getting directory...");
  587.         fflush(stdout);
  588.         while (readport() > 0 && strcmp(buf, ".") && strlen(buf) > 0)
  589.         {
  590.             printf(".");
  591.             fflush(stdout);
  592.             new = parse_menu_item(buf);
  593.             if (first)
  594.             {
  595.                 current->next = new;
  596.                 current = new;
  597.             }
  598.             else
  599.             {
  600.                 first = current = new;
  601.             }
  602.         }
  603.         close_port();
  604.         printf("\r\n");
  605.     }
  606.     if (first == NULL)
  607.     {
  608.         first = pop_menu(&i);
  609.         printf("\r\n");
  610.         printf("No menu items available.\r\n");
  611.         user_wait();
  612.     }
  613.     return first;
  614. }
  615.  
  616. /*
  617.     showmenu - show a menu given pointer to linked list 
  618. */
  619. void
  620. #ifdef KRC
  621. show_menu(g, top)
  622.     struct gopher_object *g;
  623.     int top;
  624. #else
  625. show_menu(struct gopher_object *g, int top)
  626. #endif
  627. {
  628.     int i = 1;
  629.     char *object_type;
  630.     printf("\r\n");
  631.     printf("%s\r\n", title);
  632.     if (g == NULL)
  633.     {
  634.         printf("No items available\r\n");
  635.     }
  636.     while (g)
  637.     {
  638.         if (g->name[0] == '0') object_type = "text";
  639.         else if (g->name[0] == '1') object_type = "dir";
  640.         else if (g->name[0] == '7') object_type = "indx";
  641.         else if (g->name[0] == '3') object_type = "err";
  642.         else object_type = "?";
  643.         if (i >= top) printf("%3d) %c%-65s [%s]\r\n", i, g->viewed,
  644.             g->name + 1, object_type);
  645.         if (i - top >= lines_per_page - 1) break;
  646.         i++;
  647.         g = g->next;
  648.     }
  649.     printf("Enter #=select p=previous page n=next page u=uplevel ?=help q=quit: ");
  650. }
  651.  
  652. /*
  653.     next_page - find item number of next page or back to top if last
  654. */
  655. int
  656. #ifdef KRC
  657. next_page(g, top)
  658.     struct gopher_object *g;
  659.     int top;
  660. #else
  661. next_page(struct gopher_object *g, int top)
  662. #endif
  663. {
  664.     int i;
  665.     i = 1;
  666.     while (g && i < top)
  667.     {
  668.         i++;
  669.         g = g->next;
  670.     }
  671.     if (g)
  672.     {
  673.         if (g->next == NULL)
  674.         {
  675.             return 1;
  676.         }
  677.         for (i=0; i<lines_per_page && g->next; i++)
  678.         {
  679.             top++;
  680.             g = g->next;
  681.         }
  682.     }
  683.     return top;
  684. }
  685.  
  686. /*
  687.     select_object - select an item by number
  688. */
  689. struct gopher_object *
  690. #ifdef KRC
  691. select_object(buf, menu, top)
  692.     char *buf;
  693.     struct gopher_object *menu;
  694.     int *top;
  695. #else
  696. select_object(char *buf, struct gopher_object *menu,
  697.         int *top)
  698. #endif
  699. {
  700.     int i;
  701.     char input[BUFSIZE];
  702.     struct gopher_object *g;
  703.     struct gopher_object *go;
  704.     g = menu;
  705.     if ((g = find_item(menu, buf)) == NULL) return menu;
  706.     g->viewed = '*';
  707.     if (g->name[0] == '1')
  708.     {
  709.         push_menu(menu, *top);
  710.         if (strlen(g->name) > 1) strcpy(title, g->name + 1);
  711.         g = get_menu(g);
  712.         if (g)
  713.         {
  714.             *top = 1;
  715.             return g;
  716.         }
  717.         return menu;
  718.     }
  719.     if (g->name[0] == '0') get_text(g);
  720.     else if (g->name[0] == '7')
  721.     {
  722.         printf("\r\n");
  723.         i = strlen(g->name);
  724.         if (i > 1) printf("%s\r\n", g->name + 1);
  725.         printf("Enter argument(s): ");
  726.         fflush(stdout);
  727.         i = strlen(g->path);
  728.         strcpy(input, g->path);
  729.         strcat(input, "\t");
  730.         user_input(input + i + 1, BUFSIZE - i - 2, stdin);
  731.         if (strlen(input) > i + 1)
  732.         {
  733.             go = (struct gopher_object *)malloc(
  734.                 sizeof(struct gopher_object));
  735.             if (go == NULL)
  736.             {
  737.                 printf("Error: out of memory\r\n");
  738.                 return menu;
  739.             }
  740.             go->name = strdup(g->name);
  741.             go->path = strdup(input);
  742.             go->host = strdup(g->host);
  743.             go->port = strdup(g->port);
  744.             push_menu(menu, *top);
  745.             strcpy(title, g->name + 1);
  746.             g = get_menu(go);
  747.             free(go);
  748.             if (g)
  749.             {
  750.                 *top = 1;
  751.                 return g;
  752.             }
  753.             return menu;
  754.         }
  755.     }
  756.     else
  757.     {
  758.         printf("\r\n");
  759.         printf("The format of the Gopher item that you have selected cannot be retrieved\r\n");
  760.         printf("or displayed by this gopher client.  It is probably a binary file that\r\n");
  761.         printf("contains a picture, music, or program executables.  To retrieve these types\r\n");
  762.         printf("of files you will have to run a different gopher client.  Examples of\r\n");
  763.         printf("other clients that can retrieve these types of data are Xgopher which\r\n");
  764.         printf("runs on UNIX machines and UTGopher which runs on PCs with PC/TCP.\r\n");
  765.         user_wait();
  766.     }
  767.     return menu;
  768. }
  769.  
  770. /*
  771.     free_directory - free gopher_objects in menu
  772. */
  773. void
  774. #ifdef KRC
  775. free_directory(menu)
  776.     struct gopher_object *menu;
  777. #else
  778. free_directory(struct gopher_object *menu)
  779. #endif
  780. {
  781.     struct gopher_object *g;
  782.     while (menu)
  783.     {
  784.         g = menu;
  785.         menu = menu->next;
  786.         free(g->name);
  787.         free(g->host);
  788.         free(g->path);
  789.         free(g->port);
  790.         free(g);
  791.     }
  792. }
  793.  
  794. /*
  795.     help - show help information
  796. */
  797. void help()
  798. {
  799.     char buf[BUFSIZE];
  800.     printf("\r\n");
  801.     printf("                SIMPLE GOPHER CLIENT HELP\r\n");
  802.     printf("\r\n");
  803.     printf("dir  - a directory of items that you can choose from\r\n");
  804.     printf("text - a text document you can view or email\r\n");
  805.     printf("indx - index type will request input and retrieve a directory\r\n");
  806.     printf("err  - an error that was returned by a server\r\n");
  807.     printf("?    - is displayed for all other types\r\n");
  808.     printf("\r\n");
  809.     printf("command       desc\r\n");
  810.     printf("------------- -----------------------------------------------------------\r\n");
  811.     printf("<n><enter>    type a number then return to fetch an item\r\n");
  812.     printf("n<enter>      to go to the next page of a menu\r\n");
  813.     printf("<enter>       to go to the next page of a menu\r\n");
  814.     printf("p<enter>      to go to the previous page of a menu\r\n");
  815.     printf("?<enter>      to display this information\r\n");
  816.     printf("q<enter>      to exit the gopher client\r\n");
  817.     printf("=<n><enter>   show retrieval information for item\r\n");
  818.     printf("\r\n");
  819. #ifdef GOPHADM
  820. #ifdef GOPHPHN
  821.     printf("If you need more help call %s at %s.\r\n",
  822.         GOPHADM, GOPHPHN);
  823. #endif
  824. #endif
  825.     user_wait();
  826. }
  827.  
  828. /*
  829.     show_obj_info - show information about gopher object # on menu
  830. */
  831. void
  832. #ifdef KRC
  833. show_obj_info(g, num)
  834.     struct gopher_object *g;
  835.     char *num;
  836. #else
  837. show_obj_info(struct gopher_object *g, char *num)
  838. #endif
  839. {
  840.     int i;
  841.     if ((g = find_item(g, num)) == NULL) return;
  842.     printf("\r\n");
  843.     printf("name: %s\r\n", g->name);
  844.     printf("path: %s\r\n", g->path);
  845.     printf("host: %s\r\n", g->host);
  846.     printf("port: %s\r\n", g->port);
  847.     printf("\r\n");
  848.     user_wait();
  849. }
  850.  
  851. /*
  852.     main - parse the command line and call appropriate routines
  853.         repeat until q<enter>
  854. */
  855. int
  856. #ifdef KRC
  857. main(argc, argv)
  858.     int argc;
  859.     char *argv;
  860. #else
  861. main(int argc, char *argv[])
  862. #endif
  863. {
  864.     char buf[BUFSIZE];
  865.     struct gopher_object *g;
  866.     int top = 1;
  867.     if (argc > 1) root_object.host = argv[1];
  868.     if (argc > 2) root_object.port = argv[2];
  869.     if (argc > 3)
  870.     {
  871.         printf("Format: gopher [host [port]]\r\n");
  872.     }
  873.     strcpy(title, root_object.name);
  874. #ifdef GOVCOMP
  875.     printf("---------------------------------------------------------------------------\r\n");
  876.     printf("WARNING!!  THIS IS A GOVERNMENT OWNED COMPUTER.\r\n");
  877.     printf("---------------------------------------------------------------------------\r\n");
  878.     printf("GOVERNMENT TELECOMMUNICATIONS SYSTEMS AND AUTOMATED INFORMATION SYSTEMS ARE\r\n");
  879.     printf("SUBJECT TO PERIODIC SECURITY TESTING AND MONITORING TO ENSURE PROPER\r\n");
  880.     printf("COMMUNICATIONS SECURITY (COMSEC) PROCEDURES ARE BEING OBSERVED.  USE OF THESE\r\n");
  881.     printf("SYSTEMS CONSTITUTES CONSENT TO SECURITY TESTING AND COMSEC MONITORING.\r\n");
  882.     printf("---------------------------------------------------------------------------\r\n");
  883.     printf("FOR OFFICIAL USE ONLY.\r\n");
  884.     printf("---------------------------------------------------------------------------\r\n");
  885. #endif
  886. #ifdef TIMEOUT
  887.     signal(SIGALRM, handle_alarm);
  888. #endif
  889.     fflush(stdout);
  890. #ifdef LOGFILE
  891.     get_client_info();
  892. #endif
  893.     g = get_menu(&root_object);
  894.     while (1)
  895.     {
  896.         show_menu(g, top);
  897.         fflush(stdout);
  898.         user_input(buf, BUFSIZE, stdin);
  899.         if (*buf == 'q') break;
  900.         switch (*buf)
  901.         {
  902.             case '?':
  903.                 help();
  904.                 break;
  905.             case 'u':
  906.                 free_directory(g);
  907.                 g = pop_menu(&top);
  908.                 if (g == NULL)
  909.                 {
  910.                     g = get_menu(&root_object);
  911.                     top = 1;
  912.                 }
  913.                 break;
  914.             case 'p':
  915.                 top = (top > lines_per_page)
  916.                     ? top - lines_per_page : 1;
  917.                 break;
  918.             case 'n': case '\n': case '\r': case 0:
  919.                 top = next_page(g, top);
  920.                 break;
  921.             case '=':
  922.                 show_obj_info(g, buf + 1);
  923.                 break;
  924.             default:
  925.                 g = select_object(buf, g, &top);
  926.         }
  927.     }
  928.     return 0;
  929. }
  930.